﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Metrics;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using AMRService.DAL;
using AMRService.IDAL;
using AMRService.Models.ResponseModels.ModelHelper.Common;
using Driverplugin;
using Google.Protobuf;
using Google.Protobuf.Collections;
using Grpc.Core;
using Grpc.Net.Client;
using IoTEdge.Grpc.Server.Libs.config;
using IoTEdge.Grpc.Server.Libs.entities;
using NLog;
using static Driverplugin.DeviceStatus.Types;

namespace IoTEdge.Grpc.Server.Libs.Base
{
    public abstract class AmrCollect
    {
        private ILogger logger = LogManager.GetCurrentClassLogger();
        private Metadata metadata = null;
        private ServerConfig Config = null;
        public Dictionary<string, DeviceModel> Instance = new();
        AsyncDuplexStreamingCall<Response, DeviceLifeControlRequest>? lifeControl = null;
        DriverPlugin.DriverPluginClient client = null;
        CancellationTokenSource tokenSource = new CancellationTokenSource();
        Action act = null;
        public virtual async void Start(CancellationToken token, Action act)
        {
            this.act = act;
            token.Register(async () =>
            {
                tokenSource.Cancel();
                if (Instance.Count > 0)
                {
                    foreach (var item in Instance.Keys.ToArray())
                    {
                        var model = Instance[item];
                        model.TokenSource.Cancel();
                        await Task.Delay(1000);
                    }
                }
            });
            try
            {
                this.GetServerConfig();
                await this.GetAccessToken();
                this.MainInitInstance();
            }
            catch (Exception exp)
            {
                logger.Fatal($"AMR驱动意外停止!错误消息：{exp.Message}...\n\r 堆栈信息: {exp.StackTrace}");
                return;
            }

        }
        // 实例化需要的对象
        public virtual void MainInitInstance()
        {
            logger.Info("AMR驱动开始初始化...");
            lifeControl = client.DeviceLifeControl(metadata);
            var task1 = Task.Run(handleLifeControl);
        }
        // get token
        public virtual async Task GetAccessToken()
        {
            try
            {
                metadata = new Metadata();
                var channel = GrpcChannel.ForAddress(new Uri($"http://{Config.IP}:{Config.Port}"));
                client = new DriverPlugin.DriverPluginClient(channel);
                RegistrationRequest request = new RegistrationRequest();
                request.BuildIn = false;
                request.Name = "CustomAmrProtocol";
                request.Schema = ByteString.CopyFrom(Encoding.UTF8.GetBytes(AmrDevice.GetSchema()));
                request.UiParam = ByteString.CopyFrom(Encoding.UTF8.GetBytes(AmrDevice.GetUI()));
                var response = await client.DriverRegistrationAsync(request);
                if (response.Token == "")  // 失败
                {
                    logger.Error($"AMR驱动从服务器 {Config.ToString()} 获取Token失败了！  错误信息: {response.Error}");
                    client = null;
                    return;
                }
                logger.Info($"AMR驱动成功的从IoTEdge服务器 {Config.ToString()}获取到了Token!  Token信息: {response.Token}");

                string AccessToken = response.Token;
                metadata.Add("token", AccessToken);
                var connectres = await client.ConnectAsync(new ConnectRequest(), metadata);
                if (connectres.StatusCode == Driverplugin.StatusCode.StatusOk)
                    logger.Info($"AMR驱动已成功连接到IoTEdge服务器： {Config.ToString()} ");
                else
                {
                    logger.Error($"AMR驱动连接到服务器{Config.ToString()}失败了！  错误状态码:{connectres.StatusCode} 错误信息: {connectres.ErrorMessage}");
                    client = null;
                    return;
                }
            }
            catch (Exception exp)
            {
                logger.Error($"AMR驱动发生了不可恢复错误,错误信息: {exp.Message},重启服务可能有助于解决问题！");
            }

        }
        // 获取服务器地址信息
        public virtual void GetServerConfig()
        {
            logger.Info("AMR驱动开始获取服务器配置信息！");
            var str = Environment.GetEnvironmentVariable("IoTEdgeGrpcAddr", EnvironmentVariableTarget.Machine);
            if (string.IsNullOrEmpty(str))
                throw new ArgumentException("服务器地址未设置，请检查系统环境变量[IoTEdgeGrpcAddr]!");
            var strarr = str.Split(":");
            if (strarr.Length != 2)
                throw new ArgumentException("服务器地址不合法：" + str);
            Config = Config == null ? new ServerConfig() : Config;
            Config.IP = strarr[0];
            Config.Port = int.Parse(strarr[1]);
            logger.Info($"AMR驱动获取服务器配置信息成功！信息：{Config.ToString()}");
        }
        //设备生命周期事件
        public virtual async void handleLifeControl()
        {
            try
            {
                var responseStream = lifeControl.ResponseStream;
                while (await responseStream.MoveNext(CancellationToken.None))
                {
                   DeviceLifeControlRequest req = responseStream.Current;
                    var rsp = new Response();
                    rsp.RequestID = req.RequestID;
                    rsp.StatusCode = Driverplugin.StatusCode.StatusOk;
                    switch (req.Command)
                    {
                        case DeviceLifeControlRequest.Types.DeviceLifeControlCommand.StartDevice:
                            logger.Info($"AMR驱动接收命令【StartDrivice】 【设备ID】:{req.Meta.Defination.DeviceID} 【设备名】:{req.Meta.Defination.DeviceName}");
                            StartDevice(req.Meta, ref rsp);
                            break;
                        case DeviceLifeControlRequest.Types.DeviceLifeControlCommand.StopDevice:
                            logger.Info($"AMR驱动接收命令【StopDrivice】 【设备ID】:{req.Meta.Defination.DeviceID} 【设备名】:{req.Meta.Defination.DeviceName}");
                            StopDevice(req.Meta, ref rsp);
                            break;
                        default:
                            rsp.StatusCode = Driverplugin.StatusCode.UnsupportLifeCycleRequest;
                            break;
                    }
                    try
                    {
                        // if (Instance.ContainsKey(req.Meta.Defination.DeviceID))
                        await lifeControl.RequestStream.WriteAsync(rsp);
                    }
                    catch (Exception exp)
                    {
                        logger.Error($"AMR驱动控制设备生命周期服务【handleLifeControl】意外退出！错误消息：{exp.Message},\n\r调用堆栈：{exp.StackTrace}");
                    }
                }
            }
            catch (Exception exp)
            {
                logger.Fatal($"AMR驱动控制设备生命周期服务【handleLifeControl】意外退出！错误消息：{exp.Message},\n\r调用堆栈：{exp.StackTrace}");
                //act?.Invoke();
            }
        }
        #region events
        // 按钮切换到Start
        public virtual void StartDevice(DeviceMeta device, ref Response response)
        {
            if (Instance.ContainsKey(device.Defination.DeviceID))
            {
                var x = JsonSerializer.Serialize(device);
            }
            else
            {
                var model = JsonSerializer.Deserialize<CustomProtocolModel>(device.Defination.DeviceExtensionConfig);
                // 将设备添加到设备实体
                Instance.Add(device.Defination.DeviceID, new DeviceModel()
                {
                    CustomProtocolModel = model,
                    DeviceMeta = device,
                    Interval = ConvertIntervalMS(device.Defination.CollectInterval),
                    Timeout = ConvertIntervalMS(device.Defination.Timeout)
                });
                Instance[device.Defination.DeviceID].SendMessage += SendMessageAction;
                Instance[device.Defination.DeviceID].ReportErr += SendStatus;
                Instance[device.Defination.DeviceID].run();
                Instance[device.Defination.DeviceID].TokenSource.Token.Register((x) =>
                {
                    var dev = x as DeviceModel;
                    if (Instance.ContainsKey(dev.DeviceMeta.Defination.DeviceID))
                    {
                        Instance.Remove(dev.DeviceMeta.Defination.DeviceID);
                    }
                }, Instance[device.Defination.DeviceID]);
                response.StatusCode = Driverplugin.StatusCode.StatusOk;
                logger.Info($"AMR驱动: 【设备ID】:{device.Defination.DeviceID} 已启动!");
            }
        }
        // 当按钮切换到Stop
        public virtual void StopDevice(DeviceMeta device, ref Response response)
        {  //  如果包含，则删除
            if (Instance.ContainsKey(device.Defination.DeviceID))
            {
                var dev = Instance[device.Defination.DeviceID];
                //dev.TokenSource.Cancel();
                dev.SendMessage -= SendMessageAction;
                dev.ReportErr -= SendStatus;
                logger.Info($"AMR驱动: 【设备ID】:{device.Defination.DeviceID} 已停止!");
                Instance.Remove(device.Defination.DeviceID);
                response.StatusCode = Driverplugin.StatusCode.StatusOk;
            }
            else
            {
                response.StatusCode = Driverplugin.StatusCode.StatusOk;
            }
        }
        #endregion
        #region sendmsg
        //执行报告数据
        public void SendMessageAction(DeviceModel device, Driverplugin.Data data)
        {
            try
            {
                var msger = client.TransformData(metadata);
                msger.RequestStream.WriteAsync(data);
                //logger.Info($"amr device id {device.DeviceMeta.Defination.DeviceID} report a msg!");
            }
            catch (Exception exp)
            {
                logger.Error(
                   $"AMR驱动上报数据失败,错误信息: {exp.Message},\n\r 调用堆栈：{exp.StackTrace}");
            }
        }
        // 发送状态信息
        public async void SendStatus(DeviceModel dev, DeviceState state, string msg)
        {
            HeartBeat hb = new HeartBeat();
            var status = state;
            var deviceStatus = new DeviceStatus();
            deviceStatus.State = status;
            deviceStatus.Error = msg;
            hb.DeviceStatus[dev.DeviceMeta.Defination.DeviceID] = deviceStatus;
            if (state == DeviceStatus.Types.DeviceState.ErrorState)
            {
                dev.TokenSource.Cancel();
                logger.Error($"AMR驱动检测到数据采集异常，【设备ID】{dev.DeviceMeta.Defination.DeviceID}, 【错误信息】:{msg}");
            }

            try
            {
                var msger = await client.SendHeartBeatAsync(hb, metadata);
                if (msger.StatusCode == Driverplugin.StatusCode.StatusOk)
                {
                }
                else
                {
                    logger.Error($"AMR驱动发送状态信息失败！【设备ID】 {dev.DeviceMeta.Defination.DeviceID} ,【错误代码】: {msger.StatusCode.ToString()}, 【错误信息】: {msger.ErrorMessage}");
                }
            }
            catch (Exception exp)
            {
                logger.Error($"AMR驱动回复状态信息失败！【设备ID】 {dev.DeviceMeta.Defination.DeviceID} ,【错误信息】: {exp.Message}, \n\r【调用堆栈】: {exp.StackTrace}");
            }

        }
        #endregion
        #region helper
        // 提取设置的采集时间
        public int ConvertIntervalMS(string input)
        {
            int value = 1000;

            if (input.EndsWith("ms"))
            {
                input = input.Remove(input.Length - 2, 1);
                value = int.Parse(input);
            }
            else if (input.EndsWith("s"))
            {
                input = input.Remove(input.Length - 1, 1);
                value *= int.Parse(input);
            }
            else if (input.EndsWith("m"))
            {
                input = input.Remove(input.Length - 2, 1);
                value *= int.Parse(input);
                value *= 60;
            }
            else if (input.EndsWith("h"))
            {
                input = input.Remove(input.Length - 2, 1);
                value *= int.Parse(input);
                value *= 60 * 60;
            }
            else
            {
                value *= 30;
            }

            return value;
        }
        #endregion
    }
}
